package com.googlecode.player.controller
{	
	import com.googlecode.data.PositionObject3D;
	import com.googlecode.data.markers.HideMarkerVO;
	import com.googlecode.data.markers.KeyframeVO;
	import com.googlecode.data.materials.BitmapFileMaterialVO;
	import com.googlecode.data.materials.ColorMaterialVO;
	import com.googlecode.data.materials.MaterialVO;
	import com.googlecode.data.materials.MovieMaterialVO;
	import com.googlecode.data.materials.TextMaterialVO;
	import com.googlecode.data.materials.VideoStreamMaterialVO;
	import com.googlecode.data.objects3d.CameraProxyVO;
	import com.googlecode.data.objects3d.CubeVO;
	import com.googlecode.data.objects3d.IObject3DVO;
	import com.googlecode.data.objects3d.MaterialsListObject3DVO;
	import com.googlecode.data.objects3d.Object3DVO;
	import com.googlecode.data.objects3d.PlaneVO;
	import com.googlecode.data.objects3d.TargetProxyVO;
	import com.googlecode.player.controller.mediators.SWFLoaderMediator;
	import com.googlecode.player.controller.mediators.VideoStreamMediator;
	import com.googlecode.player.data.Notifications;
	import com.googlecode.player.view.specialmaterials.TextMaterial;
	import com.googlecode.player.view.specialobjects.BillBoard;
	import com.googlecode.utils.UniqueID;
	
	import org.papervision3d.materials.BitmapFileMaterial;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.scenes.Scene3D;
	import org.puremvc.as3.multicore.interfaces.INotification;

	/**
	 * Adds a 3D object to the model. 
	 * @author USER
	 * 
	 */
	public class AddObject3DCommand extends BaseCommand
	{
		private var _object3D:IObject3DVO;
		private var _mediatorsList:Array;
		private var _videoStreamList:Array;
		
		/**
		 * Executes command. 
		 * @param notification
		 * 
		 */
		override public function execute(notification:INotification):void
		{

			
			_object3D = notification.getBody() as IObject3DVO;
			_mediatorsList = new Array();
			_videoStreamList = new Array();
			
			var scene:Scene3D = settings.scene;
			var stub:String;
			var object3D:DisplayObject3D;

			// create objects according to type
			if(_object3D is CameraProxyVO)
			{
				stub = "cameraProxy_";
			}
			
			if(_object3D is TargetProxyVO)
			{
				stub = "targetProxy_";
			}
					
			if(_object3D is PlaneVO)
			{
				stub = "plane_";
				
				object3D = handlePlane(_object3D as PlaneVO);
			}
			
			if(_object3D is CubeVO)
			{
				stub = "cube_";
				
				object3D = handleCube(_object3D as CubeVO);
			}
			
			// if the object3D has no name, make unique name
			if((_object3D as PositionObject3D).name == null)
			{
				(_object3D as PositionObject3D).name = getName(stub);
			}
			
			if(_object3D is CameraProxyVO || _object3D is TargetProxyVO)
			{
				// do nothing, these do not form part of the 3D world
			}else
			{
				// ensure the object3D in the scene has the same name, for referencing purposes
				object3D.name = (_object3D as Object3DVO).name;
				
				// transfer the rotational properties from the VO to the object3D in the scene
				object3D.rotationX = (_object3D as Object3DVO).rotationX;
				object3D.rotationY = (_object3D as Object3DVO).rotationY;
				object3D.rotationZ = (_object3D as Object3DVO).rotationZ;
			
			
		
				// deal with the keyframes
				for each(var kf:KeyframeVO in (_object3D as Object3DVO).keyframes)
				{			
					// ensure its object3DName property matches that of its parent object3D
					kf.object3DName = (_object3D as Object3DVO).name;
					
					// set the position of the object3D in the scene to the position of the keyframe at time = 0
					if(kf.time == 0)
					{
						object3D.x = kf.position.x;
						object3D.y = kf.position.y;
						object3D.z = kf.position.z;
						
						break;
					}
				}
				// deal with hideMarkers			
				for each(var hm:HideMarkerVO in (_object3D as Object3DVO).hideMarkers)
				{
					hm.object3DName = (_object3D as Object3DVO).name;
					
					if(hm.begin == 0)
					{
						object3D.visible = false;
					}
					
				}
				
				// the object3D in the scene can now be added. This action will not generate any notifications
				scene.addChild(object3D);

			}
			
			settings.addObject3D(_object3D as Object3DVO);

			
			if(_object3D is CameraProxyVO || _object3D is TargetProxyVO)
			{
				for each(var camObj:Object3DVO in settings.getObjects3D())
				{
					if(_object3D is CameraProxyVO)
					{
						if(camObj is TargetProxyVO)
						{
							updateActualCamera();
						}
					}else
					{
						if(camObj is CameraProxyVO)
						{
							updateActualCamera();
						}
					}
				}
			}
			
			settings.calculateTweens((_object3D as Object3DVO).name);

			for(i=0; i<_videoStreamList.length; i++)
			{
				facade.registerMediator(_videoStreamList[i]);
			}
			
			for(var i:uint=0; i<_mediatorsList.length; i++)
			{
				facade.registerMediator(_mediatorsList[i]);
			} 	
		}
		
		protected function updateActualCamera():void
		{
			sendNotification(Notifications.CAMERA_PROXIES_ADDED);
		}
		
		/**
		 * Generates unique name. 
		 * @param stub
		 * @return 
		 * 
		 */
		protected function getName(stub:String):String
		{
			var uniqueID:UniqueID = new UniqueID();
			
			return stub + uniqueID.generateID();
		}
		
		/**
		 * Returns a plane with visible edges. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handlePlane(VO:PlaneVO):*
		{
			var material:* = getMaterial(VO.material);
			var plane:*;
			
			if(VO.billboard)
			{
				plane = new BillBoard(material, VO.width, VO.height, VO.segmentsH, VO.segmentsW);
			}else
			{
				plane = new Plane(material, VO.width, VO.height, VO.segmentsH, VO.segmentsW);
			}
			
			
			return plane;
		}
		
		/**
		 * Returns a cube. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handleCube(VO:CubeVO):Cube
		{
			var mList:MaterialsList = new MaterialsList();
			
			
			for each(var mat:MaterialVO in VO.materialsList)
			{
				mList.addMaterial(getMaterial(mat), mat.face);
			}  

			var cube:Cube = new Cube(mList, VO.width, VO.depth, VO.depth);
			
			return cube;
		}
		
		/**
		 * Returns the actual material needed for the object3D. 
		 * @param matVO
		 * @return 
		 * 
		 */
		protected function getMaterial(matVO:MaterialVO):*
		{
			var material:*;
			
			if(matVO is BitmapFileMaterialVO)
			{
				material = new BitmapFileMaterial((matVO as BitmapFileMaterialVO).filePath);
			}
			
			if(matVO is ColorMaterialVO)
			{
				material = new ColorMaterial();
				material.fillAlpha = (matVO as ColorMaterialVO).fillAlpha;
				material.fillColor = (matVO as ColorMaterialVO).fillColor;
			}
			
			if(matVO is TextMaterialVO)
			{
				var w:uint;
				var h:uint;
				
				if(_object3D is PlaneVO)
				{
					w = (_object3D as PlaneVO).width;
					h = (_object3D as PlaneVO).height;
				}
				
				if(_object3D is CubeVO)
				{
					w = (_object3D as CubeVO).width;
					h = (_object3D as CubeVO).height;
				}
				
				var text:String = (matVO as TextMaterialVO).text;
				var font:String = (matVO as TextMaterialVO).font;
				var fontSize:uint = (matVO as TextMaterialVO).fontSize;
				var fontColor:uint = (matVO as TextMaterialVO).fontColor;
				
				material = new TextMaterial(text, font, fontSize, fontColor, w, h);
			}
			
			var clone3D:Object3DVO
			
			if(matVO is MovieMaterialVO)
			{
				material = new ColorMaterial();
				
				clone3D = _object3D.clone() as Object3DVO;
				
				if(clone3D is MaterialsListObject3DVO)
				{	
					(clone3D as MaterialsListObject3DVO).selectedFace = matVO.face;
				}
						
				_mediatorsList.push(new SWFLoaderMediator(clone3D));
			}
			
			if(matVO is VideoStreamMaterialVO)
			{
				material = new ColorMaterial();
				
				clone3D = _object3D.clone() as Object3DVO;
				
				if(clone3D is MaterialsListObject3DVO)
				{	
					(clone3D as MaterialsListObject3DVO).selectedFace = matVO.face;
				}	

				_videoStreamList.push(new VideoStreamMediator(clone3D));

			}
			
			return material;
		}
		
		/* protected function getTextDimensions(object3D:Object3DVO):Point
		{
			var dimensions:Point = new Point();
			
			if(object3D is PlaneVO)
			{
				dimensions.x = (_object3D as PlaneVO).scaleX * (_object3D as PlaneVO).width;
				dimensions.y = (_object3D as PlaneVO).scaleY * (_object3D as PlaneVO).height;		
			} 
			
			if(object3D is CubeVO)
			{	
				if((_object3D as CubeVO).selectedFace == CubeVO.BACK_FACE || (_object3D as CubeVO).selectedFace == CubeVO.FRONT_FACE)
				{
					dimensions.x = (_object3D as CubeVO).width;
					dimensions.y = (_object3D as CubeVO).height;
			
				}
				
				if((_object3D as CubeVO).selectedFace == CubeVO.TOP_FACE || (_object3D as CubeVO).selectedFace == CubeVO.BOTTOM_FACE)
				{
					dimensions.x = (_object3D as CubeVO).width;
					dimensions.y = (_object3D as CubeVO).depth;
				
				}
				
				if((_object3D as CubeVO).selectedFace == CubeVO.LEFT_FACE || (_object3D as CubeVO).selectedFace == CubeVO.RIGHT_FACE)
				{
					dimensions.x = (_object3D as CubeVO).depth;
					dimensions.y = (_object3D as CubeVO).height;
				}

			}
			
			return dimensions;
		} */
	}
}